"""
Module ``sitepal.sitebase.backends``.

Implements the custom authorization and authentication backend class for SiteBase as specified by 
the Django application ``django.contrib.auth``.
"""
from django.contrib.auth.backends       import ModelBackend
from django.contrib.auth.models         import Permission,Group
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions             import ObjectDoesNotExist
from sitepal.sitebase.models            import Entity,Tree

class AccessControlBackend(ModelBackend):
    """
    Class ``sitepal.sitebase.backends.AccessControlBackend``.
    
    Extends the default backend defined by ``django.contrib.auth`` by adding object level access 
    control.
    """
    supports_object_permissions = True

    def get_inherited_auto_group_permissions(self,group,obj):
        if isinstance(group,basestring):
            try:
                group = Group.objects.get(name__exact=group)
            except ObjectDoesNotExist:
#                print set()
                return set()
#        print "Getting inherited permissions for auto group {0} on {1}".format(group.name,obj)
        all_perms = set()
        if isinstance(obj,Tree):
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__inherits'        : True,
                'access__group__pk'       : group.id,
                'access__grantee_type__pk': ctype.id
            }
            for ances in obj.ancestors:
                kwargs['access__grantee_id'] = ances.id
                perms = Permission.objects.filter(**kwargs
                                         ).values_list('content_type__app_label','codename'
                                         ).order_by()
                all_perms.update(set(["{0}.{1}".format(ct,name) for ct,name in perms]))
#        print all_perms
        return all_perms

    def get_auto_group_permissions(self,group,obj=None):
#        print "Getting permissions for auto group {0} on {1}".format(group_name,obj)
        if isinstance(group,basestring):
            try:
                group = Group.objects.get(name__exact=group)
            except ObjectDoesNotExist:
#                print set()
                return set()
        perms = Permission.objects.filter(group__pk=group.id
                                 ).values_list('content_type__app_label','codename'
                                 ).order_by()
        all_perms = set(["{0}.{1}".format(ct,name) for ct,name in perms])
        if obj:
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__group__pk'       : group.id,
                'access__grantee_type__pk': ctype.id,
                'access__grantee_id'      : obj.id
            }
            perms = Permission.objects.filter(**kwargs
                                     ).values_list('content_type__app_label','codename'
                                     ).order_by()
            all_perms.update(["{0}.{1}".format(ct,name) for ct,name in perms])
            all_perms.update(self.get_inherited_auto_group_permissions(group,obj))
#        print all_perms
        return all_perms

    def get_inherited_group_permissions(self,user,obj):
        all_perms = set()
        if isinstance(obj,Tree):
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__inherits'        : True,
                'access__group__user__pk' : user.id,
                'access__grantee_type__pk': ctype.id,
            }
            for ances in obj.ancestors:
                kwargs['access__grantee_id'] = ances.id
                perms = Permission.objects.filter(**kwargs
                                         ).values_list('content_type__app_label','codename'
                                         ).order_by()
                all_perms.update(set(["{0}.{1}".format(ct,name) for ct,name in perms]))
        return all_perms

    def get_group_permissions(self,user,obj=None):
        cache = super(AccessControlBackend,self).get_group_permissions(user)
        cache.update(self.get_auto_group_permissions("anonymous",obj))
        if not obj or user.is_anonymous():
            return cache
        if not hasattr(obj, '_group_perm_cache'):
            obj._group_perm_cache = dict()
        if user.username not in obj._group_perm_cache:
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__group__user__pk' : user.id,
                'access__grantee_type__pk': ctype.id,
                'access__grantee_id'      : obj.id
            }
            perms = Permission.objects.filter(**kwargs
                                     ).values_list('content_type__app_label','codename'
                                     ).order_by()
            cache.update(["{0}.{1}".format(ct,name) for ct,name in perms])
            cache.update(self.get_inherited_group_permissions(user,obj))
            if not user.is_anonymous():
                cache.update(self.get_auto_group_permissions("authenticated",obj))
            if user.is_staff:
                cache.update(self.get_auto_group_permissions("staff",obj))
            if (isinstance(obj,Entity) and obj.owner_id == user.id) or obj == user:
                cache.update(self.get_auto_group_permissions("owner",obj))
            obj._group_perm_cache[user.username] = cache
            return cache
        return obj._group_perm_cache[user.username]

    def get_inherited_user_permissions(self,user,obj):
        """
        """
        all_perms = set()
        if isinstance(obj,Tree):
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__inherits'        : True,
                'access__user__pk'        : user.id,
                'access__grantee_type__pk': ctype.id
            }
            for ances in obj.ancestors:
                kwargs['access__grantee_id'] = ances.id
                perms = Permission.objects.filter(**kwargs
                                         ).values_list('content_type__app_label','codename'
                                         ).order_by()
                all_perms.update(set(["{0}.{1}".format(ct,name) for ct,name in perms]))
        return all_perms
            
    def get_all_permissions(self,user,obj=None):
        """
        Returns a list of all permission the given user has. If object is specified, the list is 
        extended by the object specific permission. General and object specific group permissions 
        are honored.

        Overrides the super class method.
        """
        cache = super(AccessControlBackend,self).get_all_permissions(self,user)
        if not obj or user.is_anonymous():
            cache.update(self.get_auto_group_permissions("anonymous",obj))
            return cache
        if not hasattr(obj, '_perm_cache'):
            obj._perm_cache = dict()
        if user.username not in obj._perm_cache:
            ctype  = ContentType.objects.get_for_model(obj)
            kwargs = {
                'access__user__pk'        : user.id,
                'access__grantee_type__pk': ctype.id,
                'access__grantee_id'      : obj.id
            }
            perms = Permission.objects.filter(**kwargs
                                     ).values_list('content_type__app_label','codename'
                                     ).order_by()
            cache.update(set(["{0}.{1}".format(ct,name) for ct,name in perms]))
            cache.update(self.get_group_permissions(user,obj))
            cache.update(self.get_inherited_user_permissions(user,obj))
            obj._perm_cache[user.username] = cache
            return cache
        return obj._perm_cache[user.username]

    def has_perm(self,user,perm,obj=None):
        """
        Returns ``True`` if the user has either the general or specific permission for the given 
        object. If object is not given, only general permissions are checked.

        Reimplements the super class method.
        """
        perms = self.get_all_permissions(user,obj)
#        print "Permission for {0} on {1}: {2}".format(user,obj,perms)
#        print "Requested permission {0} granted? {1}".format(perm,perm in perms)
        return perm in perms

